﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
//using System.Linq;
using System.Collections;

namespace FDStudio.Core.Expressions
{
    /*
     CreatedOn: 2018-01-25
     CreatedBy: Jackie Lee(天宇遊龍)
     */
    /// <summary>
    /// 条件解析器
    /// </summary>
    public class ConditionVisitor : ExpressionVisitor
    {
        StringBuilder sb;
        private Func<MemberExpression, string> _memberName;
        private Func<DateTime, string> _dateToString;
        private bool _byCondition;

        /// <summary>
        /// 解析
        /// </summary>
        /// <param name="expression"></param>
        /// <param name="memberName">成员项，默认取成员名</param>
        /// <param name="dateToString">其中日期类型格式化，默认：yyyy-MM-dd HH:mm:ss </param>
        /// <param name="byCondition">是否解析内容为条件（条件情况下，bool值变为1或0），否则原样输出</param>
        /// <returns></returns>
        public string Resolve(Expression expression, Func<MemberExpression, string> memberName, Func<DateTime, string> dateToString, bool byCondition = true)
        {
            _memberName = memberName;
            _dateToString = dateToString;
            if (_memberName == null)
            {
                _memberName = m => m.Member.Name;
            }
            if (_dateToString == null)
            {
                _dateToString = d => d.ToString("yyyy-MM-dd HH:mm:ss");
            }
            _byCondition = byCondition;
            sb = new StringBuilder();
            Visit(expression);
            return sb.ToString();
        }

        private bool _isLikeMethod; // 模糊操作

        private static Expression StripQuotes(Expression e)
        {
            while (e.NodeType == ExpressionType.Quote)
            {
                e = ((UnaryExpression)e).Operand;
            }
            return e;
        }

        protected override Expression VisitMethodCall(MethodCallExpression node)
        {
            var methodName = node.Method.Name;
            var opt = string.Empty;
            if (methodName == "Equals")
            {
                sb.Append("(");
                Visit(node.Object);
                sb.Append("=");
                Visit(node.Arguments[0]);
                sb.Append(")");
                return node;
            }
            else if (methodName == "Contains")
            {
                if (node.Arguments.Count == 2)
                {
                    var nodeArg1 = node.Arguments[0];
                    var nodeArg2 = node.Arguments[1];
                    if (nodeArg1.Type.IsGenericType && nodeArg1.Type.GetInterface("IEnumerable") != null) // 列表包含
                    {
                        Visit(nodeArg2);
                        sb.Append(" IN (");
                        Visit(nodeArg1);
                        sb.Append(")");
                    }
                    return node;
                }
                if (node.Arguments.Count == 1 && node.Type == typeof(bool) && 
                    node.Object.Type != typeof(string) && 
                    node.Object.Type.GetInterface("IEnumerable") != null) // 列表包含
                {
                    Visit(node.Arguments[0]);
                    sb.Append(" IN (");
                    Visit(node.Object);
                    sb.Append(")");
                    return node;
                }

                // 字符串包含
                sb.Append("(");
                Visit(node.Object);
                sb.Append(" LIKE ");
                _isLikeMethod = true;
                Visit(node.Arguments[0]);
                _isLikeMethod = false;
                sb.Append(")");
                return node;
            }
            return base.VisitMethodCall(node);
        }

        protected override Expression VisitBinary(BinaryExpression node)
        {
            sb.Append("(");
            Visit(node.Left);
            switch (node.NodeType)
            {
                case ExpressionType.And:
                case ExpressionType.AndAlso:
                    sb.Append(" AND ");
                    break;
                case ExpressionType.Or:
                case ExpressionType.OrElse:
                    sb.Append(" OR ");
                    break;
                case ExpressionType.Equal:
                    sb.Append("=");
                    break;
                case ExpressionType.NotEqual:
                    sb.Append("!=");
                    break;
                case ExpressionType.LessThan:
                    sb.Append("<");
                    break;
                case ExpressionType.LessThanOrEqual:
                    sb.Append("<="); break;
                case ExpressionType.GreaterThan:
                    sb.Append(">"); break;
                case ExpressionType.GreaterThanOrEqual:
                    sb.Append(">="); break;
                default: throw new NotSupportedException($"运算符{node.NodeType}不支持");
            }
            Visit(node.Right);
            sb.Append(")");
            return node;
        }

        protected override Expression VisitMember(MemberExpression node)
        {
            if (node.Expression != null)
            {
                if (node.Expression.NodeType == ExpressionType.Parameter)
                {
                    if (_byCondition && node.Type == typeof(bool))
                    {
                        sb.Append($"({_memberName(node)}=1)");
                    }
                    else
                    {
                        sb.Append(node.Member.Name);
                    }
                }
                else if (node.Expression.NodeType == ExpressionType.Constant && node.Member is FieldInfo)
                {
                    var obj = ((ConstantExpression)node.Expression).Value;
                    var value = ((FieldInfo)node.Member).GetValue(obj);

                    if (node.Type == typeof(DateTime))
                    {
                        sb.Append("'");
                        var dt = Convert.ToDateTime(value);
                        sb.Append(_dateToString(dt));
                        sb.Append("'");
                    }
                    else if (node.Type == typeof(bool))
                    {
                        sb.Append($"{(Convert.ToBoolean(value) ? 1 : 0)}");
                    }
                    else if (node.Type == typeof(string) || node.Type == typeof(char))
                    {
                        var lefOpt = _isLikeMethod ? "%" : "";
                        sb.Append($"'{lefOpt}{value}{lefOpt}'");
                    }
                    else if (node.Type.GetInterface("IEnumerable") != null)
                    {
                        IEnumerable enus = (IEnumerable)value;
                        bool hasValue = false;
                        foreach (var v in enus)
                        {
                            hasValue = true;
                            var eType = v.GetType();
                            if (eType == typeof(string) || eType == typeof(char))
                            {
                                sb.Append($"'{v}',");
                            }
                            else if (eType == typeof(DateTime))
                            {
                                sb.Append("'");
                                var dt = Convert.ToDateTime(v);
                                sb.Append(_dateToString(dt));
                                sb.Append("',");
                            }
                            else
                            {
                                sb.Append($"{v},");
                            }
                        }
                        if (hasValue)
                        {
                            sb.Remove(sb.Length - 1, 1);
                        }
                    }
                    else if("Func`2".Equals(node.Type.Name))
                    {
                        var ss = Expression.Lambda<Func<bool>>(node.Expression).Compile()();
                    }
                    else
                    {
                        sb.Append(value);
                    }
                }
                else if (node.Expression.NodeType == ExpressionType.MemberAccess)
                {
                    var outerMember = (MemberExpression)node.Expression;
                    FieldInfo fieldInfo = (FieldInfo)outerMember.Member;
                    var constant = (ConstantExpression)outerMember.Expression;
                    var name = node.Member.Name;
                    var innerObj = constant.Value;
                    object value = null;
                    var memberType = node.Type;
                    if (node.Expression.Type.IsClass)
                    {
                        var fieldObj = fieldInfo.GetValue(innerObj);
                        var pi = fieldObj.GetType().GetProperty(name);
                        value = pi.GetValue(fieldObj);
                    }
                    else
                    {
                        value = fieldInfo.GetValue(innerObj);
                    }

                    var strValue = "";
                    if (memberType == typeof(string))
                    {
                        strValue = $"'{value}'";
                    }
                    else if (memberType == typeof(bool))
                    {
                        strValue = Convert.ToBoolean(value) ? "1" : "0";
                    }
                    else if (memberType == typeof(DateTime))
                    {
                        strValue = $"'{_dateToString(Convert.ToDateTime(value))}'";
                    }
                    else
                    {
                        strValue = value.ToString();
                    }
                    sb.Append(strValue);
                }
                return node;
            }
            throw new NotSupportedException($"成员{node.Member.Name}不支持");
        }

        protected override Expression VisitUnary(UnaryExpression node)
        {
            if (node.NodeType == ExpressionType.Not)
            {
                if (node.Type == typeof(bool))
                {
                    sb.Append("(");
                    Visit(node.Operand);
                    sb.Append($"=0)");
                }
                else
                {
                    sb.Append(" (NOT ");
                    Visit(node.Operand);
                    sb.Append(")");
                }
            }
            else
                throw new NotSupportedException($"运算{node.NodeType}不支持");
            return node;
        }

        protected override Expression VisitConstant(ConstantExpression node)
        {
            if (node.Value == null)
            {
                sb.Append("NULL");
            }
            else
            {
                switch (Type.GetTypeCode(node.Value.GetType()))
                {
                    case TypeCode.Boolean:
                        sb.Append(((bool)node.Value) ? 1 : 0); break;
                    case TypeCode.String:
                        if (_isLikeMethod)
                        {
                            sb.Append($"'{node.Value}%'");
                        }
                        else
                        {
                            sb.Append($"'{node.Value}'");
                        }
                        break;
                    case TypeCode.DateTime:
                        sb.Append($"'{Convert.ToDateTime(node.Value).ToString("yyyy-MM-dd HH:mm:ss")}'"); break;
                    case TypeCode.Object:
                        throw new NotSupportedException($"常量{node.Value}不支持");
                    default:
                        sb.Append(node.Value); break;
                }
            }
            return node;
        }
    }
}
